package Docker

import (
	"autoDeploy/utils/Git"
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

//	buildDockerImage
//
// 描述信息： 构建 Docker 镜像
//
// Param：
//
//	filePath ：包含项目代码和 Dockerfile 的文件夹路径
//	userId: 用户 ID
//	teamId: 团队 ID
//	projectId: 项目 ID
//
// Return：
//
//	bool: 构建是否成功
//	string: 构建的镜像名称
//	error: 错误信息
func buildDockerImage(filePath string, userId string, teamId string, projectId string) (bool, string, error) {
	// 构建镜像名称
	imageName := fmt.Sprintf("%s_%s_%s", userId, teamId, projectId)

	// 构建 Docker 命令
	cmd := exec.Command("docker", "build", "-t", imageName, filePath)

	// 捕获命令的输出
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out

	// 执行命令
	err := cmd.Run()

	// 检查命令是否成功
	if err != nil {
		// 如果失败，返回 false 和错误信息
		return false, "", fmt.Errorf("failed to build image: %v\nOutput: %s", err, out.String())
	}

	// 如果成功，返回 true 和镜像名称
	return true, imageName, nil
}

//	deleteContainer
//
// 描述信息： 删除指定名称的 Docker 容器
//
// Param：
//
//	containerName ：容器名称
//
// Return：
//
//	bool ：操作是否成功
//	error ：错误信息
func deleteContainer(containerName string) (bool, error) {
	// 检查容器是否存在
	cmd := exec.Command("docker", "inspect", containerName)
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out

	err := cmd.Run()
	if err != nil {
		// 如果容器不存在，docker inspect 会返回错误
		if strings.Contains(out.String(), "No such object") {
			// 容器不存在，直接返回成功
			return true, nil
		}
		// 其他错误
		return false, fmt.Errorf("failed to inspect container: %v\nOutput: %s", err, out.String())
	}

	// 容器存在，删除容器
	cmd = exec.Command("docker", "rm", "-f", containerName)
	out.Reset()
	cmd.Stdout = &out
	cmd.Stderr = &out

	err = cmd.Run()
	if err != nil {
		return false, fmt.Errorf("failed to remove container: %v\nOutput: %s", err, out.String())
	}

	// 删除成功
	return true, nil
}

//	runContainer
//
// 描述信息： 根据镜像创建并运行一个容器，并将容器内的 8080 端口映射到主机的指定端口
//
// Param：
//
//	containerName ：容器名称
//	imageName ：镜像名称
//	hostPort ：主机端口号
//
// Return：
//
//	bool ：操作是否成功
//	string ：主机端口号
//	error ：错误信息
func runContainer(containerName string, imageName string, hostPort string) (bool, string, error) {
	// 构建 Docker 命令
	cmd := exec.Command("docker", "run", "-d", "--name", containerName, "-p", hostPort+":8080", imageName)

	// 捕获命令的输出
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out

	// 执行命令
	err := cmd.Run()
	if err != nil {
		// 如果命令执行失败，返回 false 和错误信息
		return false, "", fmt.Errorf("failed to run container: %v\nOutput: %s", err, out.String())
	}

	// 如果命令执行成功，返回 true 和主机端口号
	return true, hostPort, nil
}

// rollbackFromBackup
//
// 描述信息：从备份目录中恢复代码，并重新构建和运行容器。
//
// Param：
//
//	backupDir ：备份目录路径
//	userID ：用户 ID
//	teamID ：团队 ID
//	path ：目录名称
//	hostPort ：主机端口号
//
// Return：
//
//	error ：错误信息
func rollbackFromBackup(backupDir string, userID string, teamID string, path string, hostPort string) error {
	// Step 1: 确保备份目录存在
	if _, err := os.Stat(backupDir); os.IsNotExist(err) {
		return fmt.Errorf("备份目录不存在: %s", backupDir)
	}

	// Step 2: 清空工作目录
	workDir := filepath.Join(Git.BaseWorkUrl, userID, teamID, path)
	if _, err := Git.DeleteDirectory(workDir); err != nil {
		return fmt.Errorf("清空工作目录失败: %v", err)
	}

	// Step 3: 将备份目录中的内容复制到工作目录
	if err := Git.CopyDirectory(backupDir, workDir); err != nil {
		return fmt.Errorf("从备份目录复制内容到工作目录失败: %v", err)
	}

	// Step 4: 构建新的 Docker 镜像
	success, imageName, err := buildDockerImage(workDir, userID, teamID, path)
	if !success {
		return fmt.Errorf("从备份目录恢复后构建 Docker 镜像失败: %v", err)
	}

	// Step 5: 删除旧的容器（如果存在）
	containerName := fmt.Sprintf("container_%s_%s_%s", userID, teamID, path)
	_, err = deleteContainer(containerName)
	if err != nil {
		return fmt.Errorf("从备份目录恢复后删除旧容器失败: %v", err)
	}

	// Step 6: 运行新的容器
	success, _, err = runContainer(containerName, imageName, hostPort)
	if !success {
		return fmt.Errorf("从备份目录恢复后运行容器失败: %v", err)
	}

	return nil
}

// DeployFromGit
//
// 描述信息：从指定的 Git 地址拉取代码，并根据用户工作目录中的内容使用 Docker 部署，返回主机映射的端口。
//
//	如果部署失败，自动回滚到备份目录中的内容。
//
// Param：
//
//	projectURL ：项目的 Git URL 地址
//	userID ：用户 ID
//	teamID ：团队 ID
//	path ：Git 克隆时会自动生成的目录名称
//	hostPort ：主机端口号
//
// Return：
//
//	bool ：操作是否成功
//	string ：主机映射的端口
//	error ：错误信息
func DeployFromGit(projectURL string, userID string, teamID string, path string, hostPort string) (bool, string, error) {
	// 构建备份目录路径
	backupDir := filepath.Join(Git.BaseBackupUrl, userID, teamID, path)

	// Step 1: 克隆代码到用户工作目录
	if err := Git.CloneRepository(projectURL, userID, teamID, path); err != nil {
		fmt.Println("克隆代码失败开始回滚", err)
		// 如果克隆失败，尝试从备份目录恢复
		if err := rollbackFromBackup(backupDir, userID, teamID, path, hostPort); err != nil {
			return false, "", fmt.Errorf("克隆代码失败且回滚失败: %v", err)
		}
		return true, hostPort, nil
	}

	// Step 2: 构建 Docker 镜像
	workDir := filepath.Join(Git.BaseWorkUrl, userID, teamID, path)
	success, imageName, err := buildDockerImage(workDir, userID, teamID, path)
	if !success {
		// 如果构建失败，尝试从备份目录恢复
		fmt.Println("构建镜像失败开始回滚", err)
		if err := rollbackFromBackup(backupDir, userID, teamID, path, hostPort); err != nil {
			return false, "", fmt.Errorf("构建 Docker 镜像失败且回滚失败: %v", err)
		}
		return true, hostPort, nil
	}

	// Step 3: 删除旧的容器（如果存在）
	containerName := fmt.Sprintf("container_%s_%s_%s", userID, teamID, path)
	_, err = deleteContainer(containerName)
	if err != nil {
		// 如果删除容器失败，尝试从备份目录恢复
		fmt.Println("删除容器失败开始回滚")
		if err := rollbackFromBackup(backupDir, userID, teamID, path, hostPort); err != nil {
			return false, "", fmt.Errorf("删除旧容器失败且回滚失败: %v", err)
		}
		return true, hostPort, nil
	}

	// Step 4: 运行新的容器
	success, port, err := runContainer(containerName, imageName, hostPort)
	if !success {
		// 如果运行容器失败，尝试从备份目录恢复
		fmt.Println("运行容器失败开始回滚:", err)
		if err := rollbackFromBackup(backupDir, userID, teamID, path, hostPort); err != nil {
			return false, "", fmt.Errorf("运行容器失败且回滚失败: %v", err)
		}
		return true, hostPort, nil
	}

	// 返回成功信息和主机映射的端口
	return true, port, nil
}
